/*
 * Copyright (C) 2019 Honeywell, Inc. All Rights Reserved.
 */
package org.thanos.iot.service.graphic.impl;

import cn.hutool.core.util.StrUtil;
import com.honeywell.tj.iot.entity.graphic.*;
import com.honeywell.tj.iot.entity.report.ReportRealtimeData;
import com.honeywell.tj.iot.enums.graphic.FacetType;
import com.honeywell.tj.iot.enums.graphic.PaletteComponentType;
import com.honeywell.tj.iot.enums.graphic.PxviewPointType;
import com.honeywell.tj.iot.gateway.api.util.PropertyUtil;
import com.honeywell.tj.iot.mapper.graphic.*;
import com.honeywell.tj.iot.model.api.biz.ModelNodeBiz;
import com.honeywell.tj.iot.model.api.biz.ModelNodeHierarchyBiz;
import com.honeywell.tj.iot.model.api.types.code.ModelNodeTypeCode;
import com.honeywell.tj.iot.model.api.types.db.basic.Property;
import com.honeywell.tj.iot.model.api.types.vo.ModelNodeVo;
import com.honeywell.tj.iot.service.equipment.EquipmentService;
import com.honeywell.tj.iot.service.graphic.GraphicCategoryService;
import com.honeywell.tj.iot.service.graphic.GraphicService;
import com.honeywell.tj.iot.service.graphic.impl.helper.GraphicFileMapperHelper;
import com.honeywell.tj.iot.service.report.impl.RealtimeDataCacheManager;
import com.honeywell.tj.iot.vo.equipment.EquipmentVo;
import com.honeywell.tj.iot.vo.graphic.*;
import com.honeywell.tj.iot.vo.graphic.item.Item;
import com.honeywell.tj.iot.vo.graphic.item.ViewAndItems;
import com.honeywell.tj.iot.webcommons.CommonResult;
import com.honeywell.tj.iot.webcommons.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName PxViewInfoServiceImpl
 * @Author H241118
 * @Date 3/9/2020 4:05 PM
 **/
@Slf4j
@Service("graphicService")
public class GraphicServiceImpl implements GraphicService {

    @Resource
    private GraphicViewMapper graphicViewMapper;

    @Resource
    private GraphicItemMapper itemMapper;

    @Resource
    private GraphicItemPointMapper graphicItemPointMapper;

    @Resource
    private ModelNodeBiz modelNodeBiz;

    @Resource
    private ModelNodeHierarchyBiz modelNodeHierarchyBiz;

    @Resource
    private EquipmentService equipmentService;

    @Resource
    private GraphicFileMapper graphicFileMapper;

    @Resource
    private com.honeywell.tj.iot.service.graphic.GraphicCategoryService graphicCategoryService;

    @Resource
    private GraphicComponentMapper graphicComponentMapper;

    @Resource
    private GraphicComponentFileMapper graphicComponentFileMapper;

    @Resource
    private GraphicServiceManager graphicServiceManager;

    @Resource
    private GraphicFileMapperHelper graphicFileMapperHelper;

    @Resource
    private RealtimeDataCacheManager realtimeDataCacheManager;

    @Override
    public CommonResult<List<GraphicView>> getViews(Integer locationId) {
        return CommonResult.success(graphicViewMapper.selectViewsByLocationId(locationId));
    }

    @Override
    public GraphicView getView(Integer id) {
        GraphicView graphicView = graphicViewMapper.selectViewById(id);
        return Objects.isNull(graphicView) ? new GraphicView() : graphicView;
    }

    @Override
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public CommonResult createView(ViewVo view) {
        if (Objects.isNull(view.getName()) || Objects.isNull(view.getWidth()) || Objects.isNull(view.getHeight()) || Objects.isNull(view.getLocationId())) {
            return CommonResult.failed(ResultCode.VALIDATE_FAILED, ResultCode.VALIDATE_FAILED.getMessage());
        }
        GraphicView result = graphicViewMapper.selectViewByNameAndLocationId(view.getName(), view.getLocationId());
        if (Objects.nonNull(result)) {
            return CommonResult.failed(ResultCode.VALIDATE_FAILED, ResultCode.VALIDATE_FAILED.getMessage());
        } else {
            // 判断是否激活
            if (Objects.isNull(graphicViewMapper.selectActiveViewByLocationId(view.getLocationId()))) {
                view.setActive(Boolean.TRUE);
            } else {
                view.setActive(Boolean.FALSE);
            }
            graphicViewMapper.insertView(view);
            return CommonResult.success(view);
        }
    }

    @Override
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public CommonResult updateView(ViewVo viewVo) {
        if (Objects.isNull(viewVo.getId()) || Objects.isNull(viewVo.getName()) ||
                Objects.isNull(viewVo.getWidth()) || Objects.isNull(viewVo.getHeight())) {
            // 参数为空的
            return CommonResult.failed(ResultCode.VALIDATE_FAILED, ResultCode.VALIDATE_FAILED.getMessage());
        }
        GraphicView graphicView = graphicViewMapper.selectViewByName(viewVo.getName());
        if (Objects.nonNull(graphicView) && !graphicView.getId().equals(viewVo.getId())) {
            // View的名字重复, id不相同
            return CommonResult.failed(ResultCode.CONFLICT, ResultCode.CONFLICT.getMessage());
        } else {
            Boolean isActive = viewVo.getActive();
            // 如该Location下，已有active的PxView，将其置为非激活
            if (Objects.nonNull(isActive) && isActive) {
                graphicViewMapper.updateActiveViewByLocationId(viewVo.getLocationId());
            }
            int count = graphicViewMapper.updateViewById(viewVo.getName(), viewVo.getId(), viewVo.getWidth(), viewVo.getHeight(), viewVo.getLocationId(), viewVo.getActive());
            if (count == 1) {
                return CommonResult.success(viewVo);
            } else {
                return CommonResult.failed(ResultCode.FAILED, ResultCode.FAILED.getMessage());
            }
        }
    }

    @Override
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public int deleteView(Integer id) {
        itemMapper.deleteItemByViewId(id);
        GraphicView graphicView = new GraphicView(id);
        return graphicViewMapper.deleteView(graphicView);
    }

    @Override
    public GraphicView getViewAndItems(Integer id) {
        GraphicView graphicView = graphicViewMapper.selectViewAndItemsByViewId(id);
        // TODO 先写死在这里，后期修改
        // TODO 向公共List写入ViewId
        // 二者择一
        graphicView.getItems().forEach(item -> {
            Integer flag = item.getComponentId() <= PaletteComponentType.Image.getId() ? item.getComponentId() : PaletteComponentType.Image.getId();
            item.setType(flag);
        });
//        graphicView.getItems().forEach(item -> item.setType(PaletteComponentType.Image.getId()));
        List<GraphicItem> items = graphicView.getItems();
        if (CollectionUtils.isNotEmpty(items)) {
            items.forEach(item -> {
                // 获得Item中的文字模式
//                String textPattern = item.getText();
                List<String> imagePaths = graphicComponentFileMapper.selectComponentFilePathByComponentId(item.getComponentId());
                String defaultImagePath = graphicComponentFileMapper.selectPathByComponentId(item.getComponentId());
                Integer componentTypeId = graphicComponentMapper.selectComponentTypeIdById(item.getComponentId());
                if (CollectionUtils.isNotEmpty(imagePaths)) {
                    item.setImagePaths(imagePaths);
                }
                item.setDefaultImagePath(defaultImagePath)
                        .setComponentTypeId(componentTypeId);
                // 先检测Item绑定的所有点是否都存在，若不存在，则删除点和Item的绑定关系
                graphicServiceManager.checkAndDeleteUnusedPoint(item.getUuid());
                Integer pointId = graphicItemPointMapper.selectPointIdByUuid(item.getUuid());
                if (Objects.nonNull(pointId)) {
                    ModelNodeVo modelNode = modelNodeBiz.queryNode(pointId);
                    List<Property> properties = modelNode.getProperties();
                    PointVo point = new PointVo()
                            .setId(pointId)
                            .setName(modelNode.getNode().getDefaultName())
                            .setPointType(Integer.valueOf(PropertyUtil.getPropValue(properties, FacetType.Type.getCode())));
                    item.setPoint(point);
                }
            });
        }
        return Objects.isNull(graphicView) ? new GraphicView() : graphicView;
    }

    @Override
    @Transactional(rollbackFor = {RuntimeException.class, Error.class})
    public CommonResult updateItems(ViewAndItems viewAndItems) {
        // 获得该View相关联的所有组件的uuid列表
        List<String> uuids = itemMapper.selectUuidsByViewId(viewAndItems.getId());
        // 根据uuid列表，删除相关的点的绑定信息
        if (CollectionUtils.isNotEmpty(uuids)) {
            uuids.forEach(uuid ->
                    graphicItemPointMapper.deletePointItemByUuid(uuid)
            );
        }
        // 删除相关的组件
        itemMapper.deleteItemByViewId(viewAndItems.getId());
        // 批量插入全部的Item
        List<Item> items = viewAndItems.getItems();
        if (CollectionUtils.isNotEmpty(items)) {
            items.forEach(item -> {
                itemMapper.insertItem(item);
                String uuid = item.getUuid();
                Integer pointId = item.getPointId();
                if (Objects.nonNull(uuid) && Objects.nonNull(pointId)) {
                    graphicItemPointMapper.insertPointItem(new GraphicItemPoint(pointId, uuid));
                }
            });
        }
        return CommonResult.success(viewAndItems);
    }

    // itemMapper
    // graphicItemPointMapper
    // graphicComponentFileMapper
    // graphicFileMapper
    @Override
    public CommonResult<Map<String, PointInfo>> getPointInfo(List<String> uuids) {
        Map<String, PointInfo> pointInfoMap = new HashMap<>();
        uuids.forEach(uuid -> {
            PointInfo pointInfo = new PointInfo();
            // 根据UUID，获得相应的组件
            GraphicItem item = itemMapper.selectItemByUuid(uuid);
            // 获得该组件绑定的点
            Integer pointId = graphicItemPointMapper.selectPointIdByUuid(item.getUuid());
            if (Objects.nonNull(pointId)) {
                // 获得该点的实时信息
                // 使用自定义缓存
                ReportRealtimeData reportRealtimeData = realtimeDataCacheManager.getRealtimeDatum(pointId);
                String pointValue = reportRealtimeData.getPointValue();
                Integer componentId = itemMapper.selectComponentIdByUuid(uuid);
                List<GraphicComponentFile> componentFiles = graphicComponentFileMapper.selectComponentFile(componentId);
                // 获得点的类型信息，转换为PxView的类型(1.boolean/2.numeric)
                Integer pxviewPointType = graphicServiceManager.convert2PxviewPointType(reportRealtimeData.getPointTypeId());
                if (PxviewPointType.BooleanPoint.getId().equals(pxviewPointType)) {
                    // boolean点
                    Boolean value = Double.valueOf(pointValue).intValue() != 0;
                    Integer fileId = componentFiles.stream().filter(component -> {
                        Boolean logic = Double.valueOf(component.getLogic()).intValue() != 0;
                        return logic.equals(value);
                    }).findAny().get().getGraphicFileId();
//                    GraphicFile file = graphicFileMapper.selectFileById(fileId);
                    // 替换为缓存插入
                    GraphicFile file = graphicFileMapperHelper.selectFileById(fileId);
                    pointInfo.setImagePath(file.getPath());
                } else if (PxviewPointType.NumericPoint.getId().equals(pxviewPointType)) {
                    // numeric点
                    Double value = Double.valueOf(reportRealtimeData.getPointValue());
                    Integer fileId = null;
                    for (int i = 0; i < componentFiles.size(); i++) {
                        GraphicComponentFile file = componentFiles.get(i);
                        List<Double> range = Arrays.stream(file.getLogic().split(StrUtil.UNDERLINE)).map(Double::valueOf).collect(Collectors.toList());
                        if (i == 0 && value < range.get(0)) {
                            fileId = file.getGraphicFileId();
                            break;
                        } else if (i == componentFiles.size() - 1 && value > range.get(1)) {
                            fileId = file.getGraphicFileId();
                            break;
                        } else if (value >= range.get(0) && value < range.get(1)) {
                            fileId = file.getGraphicFileId();
                            break;
                        }
                    }
                    // 替换为缓存插入
                    GraphicFile file = graphicFileMapperHelper.selectFileById(fileId);
                    pointInfo.setImagePath(file.getPath());
                }
            }
            pointInfoMap.put(uuid, pointInfo);
        });
        return CommonResult.success(pointInfoMap);
    }

    @Override
    public CommonResult<List<GraphicCategoryVo>> getCategories() {
        // 获得所有的category
        List<GraphicCategoryVo> categories = graphicCategoryService.getCategoriesSelective();
        if (categories.isEmpty()) {
            return CommonResult.failed(ResultCode.FAILED, ResultCode.FAILED.getMessage());
        }
        categories.forEach(category -> {
            category.setEditable(0).setTypeId(PaletteComponentType.Folder.getId());
            List<GraphicCategoryVo> categoryVos = graphicComponentMapper.selectComponentsByCategoryIdSelective(category.getId());
            if (CollectionUtils.isNotEmpty(categoryVos)) {
                category.setChildren(categoryVos);
            }
        });
        return CommonResult.success(graphicServiceManager.getCategoryTree(categories, 0));
    }

    @Override
    public CommonResult<ComponentImageResponse> getComponentImage(Integer graphicComponentId, String uuid) {
        Integer pointId = graphicItemPointMapper.selectPointIdByUuid(uuid);
        ComponentImageResponse image = new ComponentImageResponse();
        if (Objects.isNull(pointId)) {
            // 未绑定的情况
            GraphicComponent componentInfo = graphicComponentMapper.selectComponentById(graphicComponentId);
            String imagePath = graphicComponentFileMapper.selectPathByComponentId(graphicComponentId);
            image.setPath(imagePath);
            if (Objects.nonNull(componentInfo)) {
                image.setWidth(componentInfo.getWidth()).setHeight(componentInfo.getHeight());
            }
            return CommonResult.success(image);
        } else {
            // 已绑定的情况
            GraphicComponent componentInfo = graphicComponentMapper.selectComponentById(graphicComponentId);
            ReportRealtimeData reportRealtimeData = realtimeDataCacheManager.getRealtimeDatum(pointId);
            Integer pointType = reportRealtimeData.getPointTypeId();
            Integer pxviewPointType = graphicServiceManager.convert2PxviewPointType(pointType);
            if (PxviewPointType.BooleanPoint.getId().equals(pxviewPointType)) {
                Boolean value = Double.valueOf(reportRealtimeData.getPointValue()).intValue() != 0;
                Integer componentId = itemMapper.selectComponentIdByUuid(uuid);
                List<GraphicComponentFile> componentFiles = graphicComponentFileMapper.selectComponentFile(componentId);
                Integer fileId = componentFiles.stream().filter(component -> {
                    Boolean logic = Double.valueOf(component.getLogic()).intValue() != 0;
                    return logic.equals(value);
                }).findAny().get().getGraphicFileId();
                GraphicFile file = graphicFileMapper.selectFileById(fileId);
                return CommonResult.success(image.setPath(file.getPath()).setWidth(componentInfo.getWidth()).setHeight(componentInfo.getHeight()));
            }
        }
        return null;
    }

    @Override
    public GraphicView getItems(Integer locationId) {
        Integer id = graphicViewMapper.selectActiveViewIdByLocationId(locationId);
        if (Objects.isNull(id)) {
            return new GraphicView();
        }
        return getViewAndItems(id);
    }

    @Override
    public CommonResult<List<EquipmentInfo>> getEquipments(Integer locationId) {
        List<EquipmentVo> equipments = equipmentService.getEquipments(locationId).getData();
        List<EquipmentInfo> equipmentInfos = null;
        if (CollectionUtils.isNotEmpty(equipments)) {
            equipmentInfos = equipments.stream().map(equipment -> {
                Integer equipmentId = equipment.getId();
                List<ModelNodeVo> modelNodes = modelNodeHierarchyBiz.queryChildrenByRootNodeId(equipmentId, ModelNodeTypeCode.Point.getId());
                List<BindPointInfoVo> points = null;
                if (CollectionUtils.isNotEmpty(modelNodes)) {
                    points = modelNodes.stream().map(node -> new BindPointInfoVo(node.getNode().getId(), node.getNode().getDefaultName())).collect(Collectors.toList());
                }
                return new EquipmentInfo()
                        .setEquipmentId(equipment.getId())
                        .setEquipmentName(StringUtils.join(equipment.getLocation(), StrUtil.DASHED, equipment.getName()))
                        .setPoints(points);
            }).collect(Collectors.toList());
        }
        return CommonResult.success(equipmentInfos);
    }
}
